/************************************************************************************************* 

	Name: PWM_C
	Autor: JA&AP
	
	Contains: 
				File containing all the functions related to the PWM Module.
				
				
**************************************************************************************************/
#include "pwm.h"

#define MASK_FREQ 0xF8

#define PRESCALER_1 PWM_FREQUENCY_16M
#define PRESCALER_8 PWM_FREQUENCY_2M
#define PRESCALER_64 PWM_FREQUENCY_250K
#define PRESCALER_256 PWM_FREQUENCY_62K
#define PRESCALER_1024 PWM_FREQUENCY_15K



#define PWM_R OCR0A
#define PWM_L OCR0B

#define DIR_MOTOR_PORT_R PORTG
#define DIR_MOTOR_PORT_L PORTG

#define DIR_MOTOR_DDR_R DDRG
#define DIR_MOTOR_DDR_L DDRG
#define PWM_MOTOR_DDR_R DDRB
#define PWM_MOTOR_DDR_L DDRG

#define DIR_MOTOR_PIN_R PG3
#define DIR_MOTOR_PIN_L PG4
#define PWM_MOTOR_PIN_R PB7
#define PWM_MOTOR_PIN_L PG5

//configura el timer0 en modo free running, y se mantiene comparando con el registro OCR, en caso de que halla un una comp exitosa, el valor del pin OC se coloca en 1 hasta que el timer llegue a TOP. El timer 0 tiene dos canales independientes, por eso resulta facil con un solo timer establecer el PWM
void pwm_init(unsigned char freq){

	//Set OC0A and OC0B on Compare Match (COM0x1..0 =11), Fast PWM Mode (WGM02..0=011) 
	//la inversión de señales se podria lograr desde aqui, y no como mas abajo aparece.
	TCCR0A=(1<<COM0A1) | (1<<COM0A0) | (1<<COM0B1) | (1<<COM0B0) | (1<<WGM01)| (1<<WGM00);
	pwm_setFreq(freq);
	PWM_R=0xFF;
	PWM_L=0xFF;

	//configure direction and PWM pins as outputs
	DIR_MOTOR_DDR_L |= (1<<DIR_MOTOR_PIN_L);
	DIR_MOTOR_DDR_R |= (1<<DIR_MOTOR_PIN_R);
	
	PWM_MOTOR_DDR_L |= (1<<PWM_MOTOR_PIN_L);
	PWM_MOTOR_DDR_R |= (1<<PWM_MOTOR_PIN_R);
}

/*Para setear la frecuencia del pwm, se utilizo el prescaler de la fuente de reloj del timer. 
0 es para apagar el pwm
1 es sin prescaler
2 es prescaler de 8
3 es prescaler de 64
4 es prescaler de 256
>4 es prescaler de 1024
//TODO: Investigar alguna forma de seleccionar otros rangos de frecuencia. Un buen comportamiento se logra con 4 y 5, es decir prescalers de 256 y 1024

*/
void pwm_setFreq(unsigned char freq){
	
	unsigned char temp;
	switch(freq){
		case 0:
			TCCR0B &= MASK_FREQ;
		break;
		case 1:
			temp = TCCR0B & MASK_FREQ;
			TCCR0B = temp | PRESCALER_1;
		break;
		case 2:
			temp = TCCR0B & MASK_FREQ;
			TCCR0B = temp | PRESCALER_8;
		break;
		case 3:
			temp = TCCR0B & MASK_FREQ;
			TCCR0B = temp | PRESCALER_64;
		break;
		case 4:
			temp = TCCR0B & MASK_FREQ;
			TCCR0B = temp | PRESCALER_256;
		break;

		default:
			temp = TCCR0B & MASK_FREQ;
			TCCR0B = temp | PRESCALER_1024;
		break;

	}
}

//Devuelve la freq en los mismos terminos a como se establece (0, 1, 2, 3, 4, 5)
unsigned char pwm_getFreq(){

	return TCCR0B & ~(MASK_FREQ);
}


//establecer el ancho de pulso del pwm. Se utiliza valor con signo, para manejar 
//tmabine los pines de cambio de giro.
void pwm_writeRight(signed char right){
        if(right>=0){
                #if INVERT_PWM==0
                        DIR_MOTOR_PORT_R &= ~(1<<DIR_MOTOR_PIN_R);
                #else           
                        DIR_MOTOR_PORT_R |= (1<<DIR_MOTOR_PIN_R);
                #endif
                right &= 0x7F;
        }
        else{
                #if INVERT_PWM==0
                        DIR_MOTOR_PORT_R |= (1<<DIR_MOTOR_PIN_R);
                #else 
                        DIR_MOTOR_PORT_R &= ~(1<<DIR_MOTOR_PIN_R);      
                #endif
                right = ~(right++);
        }       
        //señal invertida
        //el registro se desplaza hacia la izquierda para multiplicar el valor por dos.
        #if INVERT_PWM==1
                PWM_R = ~(right<<1);
        #else
                PWM_R = (right<<1);
        #endif

        
}
//establecer el ancho de pulso del pwm. Se utiliza valor con signo, para manejar 
//tmabine los pines de cambio de giro.
void pwm_writeLeft(signed char left){
        if(left>=0){
                #if INVERT_PWM==0
                        DIR_MOTOR_PORT_L |= (1<<DIR_MOTOR_PIN_L);
                #else
                        DIR_MOTOR_PORT_L &= ~(1<<DIR_MOTOR_PIN_L);      
                        
                #endif
                
                left &= 0x7F;
        }
        else{
                #if INVERT_PWM==0
                        DIR_MOTOR_PORT_L &= ~(1<<DIR_MOTOR_PIN_L);
                #else
                        DIR_MOTOR_PORT_L |= (1<<DIR_MOTOR_PIN_L);
                #endif
                
                left = ~(left++);
        }
        //señal invertida
        #if INVERT_PWM==1       
                PWM_L = ~(left << 1);
        #else
                PWM_L = (left << 1);
        #endif
                
}


void pwm_write(signed char left, signed char right)
{
	pwm_writeLeft(left);
	pwm_writeRight(right);
	
}
